#ifndef _Ape_IO_FS_RegularFile_h
#define _Ape_IO_FS_RegularFile_h

#include <Ape/Object.h>
#include <Ape/IO/FS/FSObject.h>
#include <Ape/EInvalidOperation.h>
#include <Ape/EIndexOutOfRange.h>
#include <Ape/Collections/Array.h>
#include <Ape/Serialization/ISink.h>


namespace Ape {
	//class EInvalidOperation;
	
	//namespace Collections {
	//	template <typename Item_t>
	//	class Array;
	//}
	namespace IO {
		namespace FS {
		
			enum AccessType {
				oNone = 0x00,
				oRead = 0x01,
				oWrite = 0x02,
				oAppend = 0x03,
				oReadBinary = 0x04,
				oWriteBinary = 0x05,
				oAppendBinary = 0x06
			};
			
			enum SeekOrigin {
				fromStart = SEEK_SET,
				fromCursor = SEEK_CUR,
				fromEnd = SEEK_END
			};
			
			class RegularFile 
				: public FSObject
				, public Ape::Serialization::ISink
			{
				TYPE_NAME ("Ape::IO::FS::RegularFile")
			protected:
				AccessType m_Mode;
				FILE* m_Fp;
				//size_t m_CurrentPosition;
				
				static const char* StringForAccessType (AccessType access);
				static bool ShouldICheckFileExistance (AccessType access);
				static bool CanIRead(AccessType access);
				static bool CanIWrite(AccessType access);
				static bool CanIReadTypedData(AccessType access);
				static bool CanIWriteTypedData(AccessType access);
				
			public:
				typedef Ape::Collections::Array<unsigned char> Buffer_t;
				
				RegularFile (const Path& path);
				RegularFile ();
				virtual ~RegularFile();
				
				void SetPath(const Path& path);
				virtual void Open (AccessType access = oReadBinary);
				void Close();
				void Flush();
				void Seek(size_t iOffset, SeekOrigin origin = fromCursor);
				size_t Tell() const;
				AccessType get_Mode() const;
				
				__attribute_warn_unused_result__
				size_t Read (Buffer_t& buff, size_t useBuffSize = 0);
				size_t Write(const Buffer_t& buff, size_t useBuffSize = 0);
				void WriteString(const String& str);
				String ReadString(int maxLen = 0);
				
				void Unlink(bool quiet = false);
				
				virtual void _Dump (bool verbose = false) const;
				
				template <typename Data_t>
				size_t WriteTyped(const Data_t data) {
					if (!CanIWriteTypedData(get_Mode()))
						throw Ape::EInvalidOperation(L"The file is closed or is opened in an incorrect mode");
					
					const size_t size = sizeof(Data_t);
					const Data_t* pData = &data;
					const unsigned char* pBytes = (const unsigned char*)pData;
					Buffer_t buff(size, '\0');
					for (size_t i = 0; i < size; i++) {
						buff[i] = pBytes[i];
					}
					return Write(buff);
				}
				
				template <typename Data_t>
				Data_t ReadTyped() {
					if (!CanIReadTypedData(get_Mode()))
						throw Ape::EInvalidOperation(L"The file is closed or is opened in an incorrect mode");
					
					const size_t size = sizeof(Data_t);
					Data_t data;
					Data_t* pData = &data;
					unsigned char* pBytes = (unsigned char*)pData;
					Buffer_t buff(size, '\0');
					size_t actuallyRead = Read(buff);
					if (actuallyRead != size)
						throw Ape::EIndexOutOfRange(L"Failed to read enough data from the file: EOF?");
					
					for (size_t i = 0; i < size; i++) {
						pBytes[i] = buff[i];
					}
					
					return data;
				}
				
				/*
				 * Begin ISink members
				 */
			protected:
				virtual size_t SubmergeBytes(const Ape::Collections::Array<unsigned char>&);
				virtual void EmergeBytes(Ape::Collections::Array<unsigned char>&);
				/*
				 * End ISink memberes
				 */
			};
			
		}
	}
}
#endif // _Ape_IO_FS_RegularFile_h


